package usermanager

import (
	"context"
	"encoding/hex"
	"errors"
	"fmt"
	"net"
	"sync"

	"time"

	"dev.ixmxm.top/rms/codes"
	"dev.ixmxm.top/rms/config"
	"dev.ixmxm.top/rms/utils/log"
	"dev.ixmxm.top/rms/utils/tools"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
)

type SwitchStatus uint8
type SwitchKey uint8
type TipCode uint8

type UserauthorManage struct {
	tcpServerAddr   string   // 服务器地址
	tcpConn         net.Conn // TCP连接
	tcpIsConnected  bool     //是否连接
	AuthorStatusMap map[SwitchKey]*AuthorStatus
	cacheMutex      sync.Mutex
	maxHour         time.Duration //允许连接失败最长时间
}

const (
	AuthorNormalKey SwitchKey = 0
	AuthorFailKey   SwitchKey = 1
)
const (
	AuthorNone   SwitchStatus = 0 //未知
	AuthorLock   SwitchStatus = 1 //锁
	AuthorUnLock SwitchStatus = 2 //解锁
	AuthorFail   SwitchStatus = 3 //连接不上Tcp服务端
)

const (
	TipCodeNone              TipCode = 0 //未知
	TipCodeDisConnectTimeOut TipCode = 1 //可能是设备拆除导致的问题,超过设定时限
)

//数据库中存储的字段
type AuthorStatus struct {
	Status     SwitchStatus `json:"status" bson:"status"`
	UpdateTime time.Time    `json:"update_time" bson:"update_time"`
	IsNullFlag bool         `json:"-" bson:"-"`
}

//利用缓存数据判断是否需要提示
func (m *UserManager) GetTipCode() TipCode {
	if config.Conf.AuthorModel.Switch == 1 {
		authorFailStatu, find := m.Userauthor.AuthorStatusMap[AuthorFailKey]
		if !find {
			return TipCodeNone
		}
		if authorFailStatu.IsNullFlag {
			return TipCodeNone
		}
		if time.Now().Sub(authorFailStatu.UpdateTime) > m.Userauthor.maxHour {
			return TipCodeDisConnectTimeOut
		}
	}
	return TipCodeNone
}

type AuthorInfo struct {
	Status     SwitchStatus `json:"status" `
	UpdateTime string       `json:"update_time" `
	FailedTime string       `json:"failed_time" `
}

//利用缓存数据判断是否需要提示
func (m *UserManager) GetAuthorInfo() *AuthorInfo {
	authorInfo := &AuthorInfo{
		Status:     AuthorNone,
		UpdateTime: "",
		FailedTime: "",
	}
	authorStatu, find1 := m.Userauthor.AuthorStatusMap[AuthorNormalKey]
	authorFailStatu, find2 := m.Userauthor.AuthorStatusMap[AuthorFailKey]

	if find1 && !find2 {
		authorInfo.Status = authorStatu.Status
		if authorStatu.IsNullFlag {
			authorInfo.UpdateTime = ""
		} else {
			authorInfo.UpdateTime = tools.GetCurrentTiemByshanghai1(authorStatu.UpdateTime).Format("2006-01-02 15:04:05")
		}
		if authorFailStatu.IsNullFlag {
			authorInfo.FailedTime = ""
		} else {
			authorInfo.FailedTime = tools.GetCurrentTiemByshanghai1(authorFailStatu.UpdateTime).Format("2006-01-02 15:04:05")
		}

	} else if find2 {
		//authorInfo.Status = authorFailStatu.Status
		authorInfo.Status = authorStatu.Status
		if authorStatu.IsNullFlag {
			authorInfo.UpdateTime = ""
		} else {
			authorInfo.UpdateTime = tools.GetCurrentTiemByshanghai1(authorStatu.UpdateTime).Format("2006-01-02 15:04:05")
		}
		if authorFailStatu.IsNullFlag {
			authorInfo.FailedTime = ""
		} else {
			authorInfo.FailedTime = tools.GetCurrentTiemByshanghai1(authorFailStatu.UpdateTime).Format("2006-01-02 15:04:05")
			//authorInfo.FailedTime = GetCurrentTiemByshanghai1(authorFailStatu.UpdateTime).Format("2006-01-02 15:04:05")
		}

	}
	return authorInfo
}

func (m *UserManager) IsAuthorLock() bool {
	if config.Conf.AuthorModel.Switch == 1 {
		authorStatu, find := m.Userauthor.AuthorStatusMap[AuthorNormalKey]
		if !find {
			return false
		}
		if authorStatu.IsNullFlag {
			return false
		}
		if authorStatu.Status == AuthorLock {
			return true
		}
	}
	return false
}

// 初始化缓存
func (m *UserManager) InitCache(ctx context.Context) error {
	m.Userauthor.AuthorStatusMap = make(map[SwitchKey]*AuthorStatus, 0)

	//查询数据库
	//1.正常
	//m.queryDatabase(ctx)
	authorStatus, err := m.QueryDatabase(ctx, m.Userauthor.TableNameSwitch())
	if err != nil {
		return err
	}
	log.Warnf("%+v", authorStatus)
	m.Userauthor.AuthorStatusMap[AuthorNormalKey] = authorStatus

	//2.异常
	authorStatus2, err := m.QueryDatabase(ctx, m.Userauthor.TableNameFail())
	if err != nil {
		return err
	}
	m.Userauthor.AuthorStatusMap[AuthorFailKey] = authorStatus2

	return nil
}
func (m *UserManager) UpdateLockCatch(authorStatu *AuthorStatus) {

	m.Userauthor.AuthorStatusMap[AuthorNormalKey] = authorStatu
}

//连接服务器并进行操作
func (m *UserManager) ConnectServer(ctx context.Context) {
	//初始化Tcp对象
	//m.InitUserAuthor()
	//初始化缓存
	m.InitCache(ctx)
	for {
		//连接服务端
		err := m.HandConnect()
		//连接失败
		if err != nil {
			//添加指令到Fail数据库
			m.AddLabelToDB(ctx, AuthorFail, m.Userauthor.TableNameFail(), time.Now())
			//更新缓存
			m.StoreDBDataToFailCache(AuthorFail)
			m.GetTipCode()
			// time, ok := m.GetFirstFailureTime()
			// if ok {
			// 	//判断是否插入Lock
			// 	m.InsertDataIfNeeded(ctx, time)
			// 	//有Lock标记时关闭RMS系统
			// }
			m.IsAuthorLock()
		}
		//连接成功
		for m.Userauthor.tcpIsConnected {
			//清空Fail数据库
			m.ClearDB(ctx, m.Userauthor.TableNameFail())
			//更新缓存
			m.InitCache(ctx)

			buffer := m.HandReceiveFromServer()
			//验证报文是否正确
			err1 := m.CheckData(buffer)
			if err1 != nil {
				continue
			}
			//根据接收信息拼接回复报文
			msgSendServer, err := m.HandleData(ctx, buffer)
			if err != nil {
				fmt.Printf("处理来自服务端的消息时出现异常")
				continue
			}
			//将发送的报文转为ASCII
			asciiCode, err := m.StringToASCII(msgSendServer)
			//发送报文
			m.SendToServer(asciiCode)
			if err != nil {
				println("Send error:", err.Error())
				continue
			}
		}
		time.Sleep(2 * time.Second)
	}
}

// HandConnect 尝试连接到服务器，并在失败时进行重试
func (m *UserManager) HandConnect() error {
	var err error
	m.Userauthor.tcpServerAddr = config.Conf.AuthorModel.TcpServerAddr
	m.Userauthor.maxHour = time.Duration(config.Conf.AuthorModel.MaxHour) * time.Hour
	m.Userauthor.tcpConn, err = net.Dial("tcp", m.Userauthor.tcpServerAddr)
	if err == nil {
		//fmt.Println("Successfully connected to the server.")
		m.Userauthor.tcpIsConnected = true
		return nil
	}
	defer m.Close()
	m.Userauthor.tcpIsConnected = false
	//fmt.Printf("Unable to connect to server: %v. Retrying in %v...\n", err, m.userauthor.tcpRetryDelay)
	return err
}

// HandReceive 接收从服务端发送的数据
func (m *UserManager) HandReceiveFromServer() []byte {
	buffer := make([]byte, 16)
	_, err := m.Userauthor.tcpConn.Read(buffer)
	if err != nil {
		//fmt.Println("Error receiving data:", err)
		m.Userauthor.tcpIsConnected = false
		//关闭连接
		m.Userauthor.tcpConn.Close()
	}
	return buffer
}

//检验数据是否合法
func (m *UserManager) CheckData(buffer []byte) error {
	valid := true
	// 验证前4位报文是否都是FF
	prefix := []byte{0xFF}
	for i := 0; i < 4; i++ {
		if buffer[i] != prefix[0] {
			valid = false
			return errors.New("数据验证异常")
		}
	}
	//验证中间8位是否为01或00
	prefix1 := []byte{0x00, 0x01}
	for i := 4; i < 12; i++ {
		if buffer[i] != prefix1[0] && buffer[i] != prefix1[1] {
			valid = false
			return errors.New("数据验证异常")
		}
	}
	prefix3 := []byte{0xEE}
	for i := 12; i < 16; i++ {
		if buffer[i] != prefix3[0] {
			valid = false
			return errors.New("数据验证异常")
		}
	}
	if valid {
		str := hex.EncodeToString(buffer)
		fmt.Printf("接收的报文为:%v\n", str)
		return nil
	}
	return errors.New("数据验证异常")
}

// Send 发送数据到服务器
func (m *UserManager) SendToServer(buffer []byte) error {
	_, err := m.Userauthor.tcpConn.Write(buffer)
	if err != nil {
		//fmt.Println("Error sending data:", err)
	}
	return err
}

// Close 关闭客户端连接
func (m *UserManager) Close() error {
	if m.Userauthor.tcpConn != nil {
		err := m.Userauthor.tcpConn.Close()
		if err != nil {
			return err
		}
	}
	return nil
}

//拼接回复报文并在数据库中写入指令
func (m *UserManager) HandleData(ctx context.Context, buffer []byte) (string, error) {
	var strSlice []string
	//拼接报文
	for i := 0; i < 4; i++ {
		strSlice = append(strSlice, "EE")
	}
	//回复收到消息
	//数据判断
	//锁机指令
	switch buffer[5] {
	case 0x00:
		//回复报文
		strSlice = append(strSlice, "00")
	case 0x01:
		//清空数据库中的UnLock指令
		if m.Userauthor.AuthorStatusMap[AuthorNormalKey].Status == AuthorUnLock {
			//清空数据库
			m.ClearDB(ctx, m.Userauthor.TableNameSwitch())
		}
		//往数据库中写入Lock
		errLock := m.AddLabelToDB(ctx, AuthorLock, m.Userauthor.TableNameSwitch(), time.Now())
		if errLock != nil {
			//fmt.Printf("数据库%v中写入Lock失败", m.userauthor.TableName())
			return "", errLock
		} else {
			//更新缓存
			m.StoreDBDataToSwitchCache(AuthorLock)
			//从缓存中读取是否有Lock,有则执行锁机操作
			if m.IsAuthorLock() {
				//执行成功后
				strSlice = append(strSlice, "01")
			}

		}
	}
	//降速功能，暂时没有，回复00
	strSlice = append(strSlice, "00")

	//恢复指令
	switch buffer[7] {
	case 0x00:
		strSlice = append(strSlice, "00")
	case 0x01:
		//先判断缓存区中的指令是UnLock还是Lock
		if m.Userauthor.AuthorStatusMap[AuthorNormalKey].Status == AuthorLock {
			//清空数据库
			m.ClearDB(ctx, m.Userauthor.TableNameSwitch())
		}
		//往数据库中写入UnLock指令
		errLock := m.AddLabelToDB(ctx, AuthorUnLock, m.Userauthor.TableNameSwitch(), time.Now())
		if errLock != nil {
			//fmt.Printf("数据库%v中写入Lock失败", m.userauthor.TableName())
			return "", errLock
		} else {
			//写入成功则更新缓存
			m.StoreDBDataToSwitchCache(AuthorUnLock)
		}
		if !m.IsAuthorLock() {
			//执行成功后
			strSlice = append(strSlice, "01")
		}
	}
	for i := 7; i < 11; i++ {
		strSlice = append(strSlice, "00")
	}
	//服务器回复收到指令
	if strSlice[4] == "01" || strSlice[6] == "01" {
		strSlice = append(strSlice, "01")
	} else {
		strSlice = append(strSlice, "00")
	}
	//拼接报文
	for i := 12; i < 16; i++ {
		strSlice = append(strSlice, "FF")
	}
	//将切片拼接为字符串
	var str string
	for i := 0; i < len(strSlice); i++ {
		str = str + strSlice[i]
	}
	return str, nil
}

// 将字符串转换为ASCII码
func (m *UserManager) StringToASCII(s string) ([]byte, error) {
	asciiBytes := make([]byte, len(s))

	for i, char := range s {
		asciiBytes[i] = byte(char)
	}
	return asciiBytes, nil
}

//往数据库中写入指令
func (m *UserManager) AddLabelToDB(ctx context.Context, status SwitchStatus, dbName string, timeFail time.Time) codes.Code {
	m.Userauthor.cacheMutex.Lock()
	defer m.Userauthor.cacheMutex.Unlock()
	existingRecord, err := m.db.Collection(ctx, dbName).CountDocuments(ctx, bson.M{"status": status})
	// 检查数据库中是否存在信息
	if err != nil {
		return codes.ErrDatabase.Wrap(err)
	}
	// 如果没有失败信息，则进行写入
	if existingRecord == 0 {
		authorStatus := &AuthorStatus{Status: status, UpdateTime: timeFail}
		_, dbRes := m.db.Collection(ctx, dbName).InsertOne(ctx, authorStatus)
		if dbRes != nil {
			log.ErrorfWithContext(ctx, "Insert status:%s to db error:%s", status, dbRes.Error())
			return codes.ErrDatabase.Wrap(dbRes)
		}
		//更新缓存
		if dbName == m.Userauthor.TableNameSwitch() {
			m.Userauthor.AuthorStatusMap[AuthorNormalKey] = authorStatus
		} else if dbName == m.Userauthor.TableNameFail() {
			m.Userauthor.AuthorStatusMap[AuthorFailKey] = authorStatus
		}

	}
	return nil
}

//根据信息查询数据库
func (m *UserManager) QueryDatabaseByMsg(ctx context.Context, status SwitchStatus, dbName string) (*AuthorStatus, error) {
	m.Userauthor.cacheMutex.Lock()
	defer m.Userauthor.cacheMutex.Unlock()
	filter := bson.M{"status": status}
	// 执行查询
	dbRes := m.db.Collection(ctx, dbName).FindOne(ctx, filter)
	if dbRes.Err() != nil && !errors.Is(dbRes.Err(), mongo.ErrNoDocuments) {
		log.ErrorfWithContext(ctx, "get unfinished task by dependExternalTaskCode from db error:%s", dbRes.Err().Error())
		return nil, codes.ErrDatabase.Wrap(dbRes.Err())
	}
	authorStatus := new(AuthorStatus)
	if dbRes.Err() != nil && errors.Is(dbRes.Err(), mongo.ErrNoDocuments) {
		authorStatus.IsNullFlag = true
		return authorStatus, nil
	}
	authorStatus.IsNullFlag = false
	if errBind := dbRes.Decode(authorStatus); errBind != nil {
		log.ErrorfWithContext(ctx, "bind robot assign task from db error:%s", errBind.Error())
		return nil, codes.ErrDatabase.Wrap(errBind)
	}
	return authorStatus, nil
	// if err != nil {
	// 	log.ErrorfWithContext(ctx, "query from db error:%s", err.Error())
	// 	return nil, err
	// }

	//
	// for dbRes.Next(context.Background()) {
	// 	var result bson.M
	// 	if err := dbRes.Decode(&result); err != nil {
	// 		return nil, err
	// 	}
	// 	timeFailPrimitive, ok := result["timeFail"].(primitive.DateTime)
	// 	if !ok {
	// 		continue
	// 	}
	// 	timeFail := timeFailPrimitive.Time()

	// 	// 获取并转换 status 字段为字符串
	// 	status1, ok := result["status"]
	// 	if !ok {
	// 		continue
	// 	}
	// 	a := authorStatus{
	// 		time:   timeFail,
	// 		status: uint8(status1),
	// 	}
	// 	author = append(author, a)
	// }
	// return author, nil
	// if errBind := dbRes.All(ctx, &author); errBind != nil {
	// 	log.ErrorfWithContext(ctx, "bind all author status error:%s", errBind.Error())
	// 	return nil, err
	// }
	// return author, nil
}

//查询数据库
func (m *UserManager) QueryDatabase(ctx context.Context, dbName string) (*AuthorStatus, codes.Code) {
	m.Userauthor.cacheMutex.Lock()
	defer m.Userauthor.cacheMutex.Unlock()
	//filter := bson.M{"status": str}
	// 执行查询
	dbRes := m.db.Collection(ctx, dbName).FindOne(ctx, bson.M{})
	if dbRes.Err() != nil && !errors.Is(dbRes.Err(), mongo.ErrNoDocuments) {
		log.ErrorfWithContext(ctx, "get unfinished task by dependExternalTaskCode from db error:%s", dbRes.Err().Error())
		return nil, codes.ErrDatabase.Wrap(dbRes.Err())
	}
	authorStatus := new(AuthorStatus)
	if dbRes.Err() != nil && errors.Is(dbRes.Err(), mongo.ErrNoDocuments) {
		authorStatus.IsNullFlag = true
		return authorStatus, nil
	}
	authorStatus.IsNullFlag = false
	if errBind := dbRes.Decode(authorStatus); errBind != nil {
		log.ErrorfWithContext(ctx, "bind robot assign task from db error:%s", errBind.Error())
		return nil, codes.ErrDatabase.Wrap(errBind)
	}
	return authorStatus, nil
}

//数据库表名称
func (UserauthorManage) TableNameSwitch() string {
	return "t_data_sys_users_authorSwicth"
}

func (UserauthorManage) TableNameFail() string {
	return "t_data_sys_users_authorFail"

}

//更新Switch缓存
func (m *UserManager) StoreDBDataToSwitchCache(status SwitchStatus) error {
	// 查询数据库并获取结果
	authorStatus, err := m.QueryDatabaseByMsg(context.Background(), status, m.Userauthor.TableNameSwitch())
	if err != nil {
		return err
	}
	// 遍历查询结果，将数据存入缓存
	m.Userauthor.AuthorStatusMap[AuthorNormalKey] = authorStatus
	// key1 := "fail_time"
	// m.Userauthor.switchTimeCache[key1] = authorStatus.time

	// key2 := "status"
	// m.Userauthor.messageSwitchCache[key2] = authorStatus.status

	return nil
}

//更新Fail缓存
func (m *UserManager) StoreDBDataToFailCache(status SwitchStatus) error {
	// 查询数据库并获取结果
	authorStatus, err := m.QueryDatabaseByMsg(context.Background(), status, m.Userauthor.TableNameFail())
	if err != nil {
		return err
	}
	// 遍历查询结果，将数据存入缓存
	m.Userauthor.AuthorStatusMap[AuthorFailKey] = authorStatus
	// key1 := "fail_time"
	// m.Userauthor.failureTimeCache[key1] = authorStatus.time

	// key2 := "status"
	// m.Userauthor.messageFailCache[key2] = authorStatus.status

	return nil
}

//清空数据库
func (m *UserManager) ClearDB(ctx context.Context, dbName string) error {
	_, errDelete := m.db.Collection(ctx, dbName).DeleteMany(ctx, bson.M{})
	if errDelete != nil && !errors.Is(errDelete, mongo.ErrNoDocuments) {
		return errDelete
	}
	return nil
}

// 获取失败记录的时间，从缓存中读取
func (m *UserManager) GetFirstFailureTime() (time.Time, bool) {
	// 从缓存中读取数据
	m.Userauthor.cacheMutex.Lock()
	defer m.Userauthor.cacheMutex.Unlock()
	// 假设缓存中的键为 "fail_time"，你可以根据实际情况修改
	// 尝试从缓存中获取数据
	if timeFail, ok := m.Userauthor.AuthorStatusMap[AuthorFailKey]; ok {
		return timeFail.UpdateTime, true
	}
	// 如果缓存中没有数据，返回一个零值和 false
	return time.Time{}, false
}

